﻿using System;
using System.Text;
using SoftwareConsulting.BI11.NumericalMethods.Interfaces;
using SoftwareConsulting.BI11.NumericalMethods.MatrixAlgebra;

namespace SoftwareConsulting.BI11.NumericalMethods.Optimization
{
    /// <summary>
    /// Класс генетического алгоритма для многоаргументных функций
    /// </summary>
    public class VectorGeneticAlgorithm : GeneticAlgorithm
    {	    
        private SuspiciousOptimalVector[] _bestPoints;//лучшие точки	    
        private int _filledPointsCount;//число заполненных точек

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="function">Функция, требующая решения</param>
        /// <param name="pointsCreator">Фабрика точек</param>
        /// <param name="chromosomeController">Контроллер хромосом</param>
        public VectorGeneticAlgorithm(IManyVariableFunction function, SuspiciousOptimalPointAndVectorFactory pointsCreator, ChromosomeController chromosomeController)
                    : base(function, pointsCreator, chromosomeController)
        {
        }

        /// <summary>
        /// Добавляет указанную точку популяции
        /// </summary>
        /// <param name="newPoint"></param>
        public override void AddPoint(object newPoint)
        {
            SuspiciousOptimalVector v = PointsFactory.CreateVector((Vector)newPoint, Function);
            if (_filledPointsCount == 0 || _bestPoints[_filledPointsCount - 1].BetterThan(v))
            {
                _bestPoints[_filledPointsCount++] = v;
                return;
            }
            int n = 0;
            int m = _filledPointsCount - 1;
            if (_bestPoints[0].BetterThan(v))
            {
                int k;
                while (m - n > 1)
                {
                    k = (n + m) / 2;
                    if (v.BetterThan(_bestPoints[k]))
                        m = k;
                    else
                        n = k;
                }
                n = m;
            }
            for (m = _filledPointsCount; m > n; m--)
                _bestPoints[m] = _bestPoints[m - 1];
            _bestPoints[n] = v;
            _filledPointsCount += 1;
        }

        /// <summary>
        /// Возвращает результат
        /// </summary>
        public override double[] Result
        {
            get 
            { 
                return _bestPoints[0].Argument; 
            }
        }

        /// <summary>
        /// Возвращает указанную особь
        /// </summary>
        /// <param name="individualIndex"></param>
        /// <returns></returns>
        public override object GetIndividualAt(int individualIndex)
        {
            try 
            { 
                return new Vector(_bestPoints[individualIndex].Argument); 
            }
            catch (OverflowException) 
            { 
                return null; 
            };
        }

        /// <summary>
        /// Инициализирует итерации
        /// </summary>
        /// <param name="initialPopulationSize"></param>
        public override void InitializeIterations(int initialPopulationSize)
        {
            _bestPoints = new SuspiciousOptimalVector[initialPopulationSize];
        }

        /// <summary>
        /// Возвращает случайное целочисленное значение fitness-показателя
        /// </summary>
        /// <returns></returns>
        public override double[] GetRandomIntegerFitness()
        {
            double[] f = new double[_bestPoints.Length];
            double sum = 0;
            for (int i = 0; i < _bestPoints.Length; i++)
            {
                f[i] = _bestPoints[i].Value + sum;
                sum += _bestPoints[i].Value;
            }
            sum = 1 / sum;
            for (int i = 0; i < _bestPoints.Length; i++)
                f[i] *= sum;
            return f;
        }

        /// <summary>
        /// Сбрасывает популяцию
        /// </summary>
        public override void Reset()
        {
            _filledPointsCount = 0;
        }

        /// <summary>
        /// Возвращает как строку
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(_bestPoints[0]);
            for (int i = 1; i < Math.Min(_bestPoints.Length, 30); i++)
            {
                sb.Append('\n');
                sb.Append(_bestPoints[i]);
            }
            return sb.ToString();
        }
    }
}
